package com.dex4d.ui.main.model;

import android.app.Activity;
import android.text.TextUtils;

import com.dex4d.BuildConfig;
import com.dex4d.constants.IConstants;
import com.dex4d.core.CustomApplication;
import com.dex4d.core.http.DefaultTransformer;
import com.dex4d.core.http.JsonApiWrapper;
import com.dex4d.core.subscribers.ProgressSubscriber;
import com.dex4d.model.D4DTxLogModel;
import com.dex4d.model.HttpResult;
import com.dex4d.model.RequestParam;
import com.dex4d.model.User;
import com.dex4d.ui.qr.model.QRPay;
import com.dex4d.ui.qr.model.QRRequest;
import com.dex4d.ui.qr.model.QRResult;
import com.dex4d.ui.qr.utils.AddressUtils;
import com.dex4d.ui.wallet.model.WalletTx;
import com.dex4d.utils.CryptoUtils;
import com.dex4d.utils.EthWalletUtil;
import com.dex4d.utils.Log;
import com.dex4d.utils.SPUtil;

import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import rx.Observable;
import rx.functions.Func1;
import rx.functions.Func2;

import static com.framework.bigdecimalutils.BigDecimalUtils.is;

/**
 * 作者    LiQingyi
 * 时间    2018/11/2 15:58
 * 文件    dex4d-android
 * 描述
 */
public class Dex4dTrade {

    public static final String ETH = "eth";
    public static final int P_ANDROID = 0;//Android
    public static final int P_WEB_QR = 1;//WEB_QR

    //转账相关字段
    private OnTransferListener transferListener;

    public void setTransferListener(OnTransferListener transferListener) {
        this.transferListener = transferListener;
    }

    //扫码相关字段
    private int platform; //处理平台
    private String qrDesc; //操作方式购买，卖出等
    private String qrURl;//扫码后得到数据，此处为URL，解析之后需要需处使用
    private QRPay.OnQRPayListener qrPayListener;//支付处理监听
    private String referral;

    private String swapFrom;//来自
    private String swapTo;//去向

    /**
     * 绑定监听
     *
     * @param qrPayListener
     */
    public void setQrPayListener(QRPay.OnQRPayListener qrPayListener) {
        this.qrPayListener = qrPayListener;
    }

    public void setPlatform(int platform) {
        this.platform = platform;
    }

    public void setQrDesc(String qrDesc) {
        this.qrDesc = qrDesc;
    }

    public String getQrDesc() {
        return qrDesc;
    }

    public void setQrURl(String qrURl) {
        this.qrURl = qrURl;
    }

    public String getReferral() {
        return referral;
    }

    public void setReferral(String referral) {
        this.referral = referral;
    }

    public void setSwapFrom(String swapFrom) {
        this.swapFrom = swapFrom;
        if (!TextUtils.isEmpty(swapFrom)) {
            this.symbol = swapFrom;
        }
    }

    public void setSwapTo(String swapTo) {
        this.swapTo = swapTo;
    }

    public String getSwapFrom() {
        return swapFrom;
    }

    public String getSwapTo() {
        return swapTo;
    }

    private Activity activity;
    private String symbol;//代币符号
    private OnTradeListener listener;
    private String pwd; //密码

    private String tokenAmount;  //Dex4d数量
    private String coinAmount;   //购买DEX4D时需要花费的代币(ETH\SEELE\OMG等)数量

    private String assetBalance;  //资产余额
    private String mTokenContractAddress; //token的合约地址

    private String spenderAddress;

    private BigInteger nonce;
    private D4DTxLogModel mTxLogModel;
    public String getSpenderAddress() {
        return spenderAddress;
    }

    public String getSymbol() {
        return symbol;
    }

    public void setSymbol(String symbol) {
        this.symbol = symbol.toLowerCase();
    }

    public void setSpenderAddress(String spenderAddress) {
        this.spenderAddress = spenderAddress;
    }

    public void setAssetBalance(String assetBalance) {
        this.assetBalance = assetBalance;
    }

    public String getAssetBalance() {
        return assetBalance;
    }

    public String getTokenContractAddress() {
        return mTokenContractAddress;
    }

    public void setTokenContractAddress(String tokenContractAddress) {
        this.mTokenContractAddress = tokenContractAddress;
    }

    public Dex4dTrade(Activity activity, String symbol) {
        this.activity = activity;
        this.symbol = symbol;
        this.pwd = User.getInstance().getPassphrase(); //解析密码
        this.platform = P_ANDROID;//默认为客户端平台
    }

    public Dex4dTrade(Activity activity) {
        this.activity = activity;
        this.pwd = User.getInstance().getPassphrase(); //解析密码
        this.platform = P_ANDROID;
    }

    @Override
    public String toString() {
        return "Dex4dTrade{" +
                "tokenAmount='" + tokenAmount + '\'' +
                ", coinAmount='" + coinAmount + '\'' +
                ", assetBalance='" + assetBalance + '\'' +
                '}';
    }

    public String getTokenAmount() {
        return tokenAmount;
    }

    public String getCoinAmount() {
        return coinAmount;
    }

    /**
     * 设置购买Token数量
     *
     * @param tokenAmount
     */
    public void setTokenAmount(String tokenAmount) {
        this.tokenAmount = tokenAmount;
    }

    /**
     * 设置花费代币数量
     *FsetTokenAmount
     * @param coinAmount
     */
    public void setCoinAmount(String coinAmount) {
        this.coinAmount = coinAmount;
    }

    /**
     * 判断是否可以进行购买
     *
     * @return
     */
    public boolean isBuy() {
        BigDecimal bigDecimal = Convert.fromWei(coinAmount, Convert.Unit.ETHER);
        Log.i(bigDecimal.toString());
        return is(tokenAmount).isPositive()//资产数量大于0
                && (is(bigDecimal).isPositive()//代币数量大于0
                && is(bigDecimal).lt(getAssetBalance()));//花费数量小于资产余额
    }

    /**
     * 设置回调监听函数
     *
     * @param listener
     */
    public void setOnTradeListener(OnTradeListener listener) {
        this.listener = listener;
    }

    /**
     * App提交交易
     *
     * @param pwd
     * @param limit
     * @param function
     */
    private void raw(final String pwd,
                     final BigInteger value,
                     final String limit,
                     final Function function,
                     final String contractAddress,
                     final boolean isApprove) {
        rawSign(pwd, value, limit, function, contractAddress)
                .flatMap(new Func1<String, Observable<HttpResult<String>>>() {
                    @Override
                    public Observable<HttpResult<String>> call(String sign) {
                        //数据签名，提交交易
                        final RequestParam txParam = new RequestParam();
                        txParam.setId(3);
                        txParam.setJsonrpc(IConstants.JSON_RPC_VERSION);
                        txParam.setMethod(IConstants.CONTRACTSERVICE_SENDRAWTRANSACTION);
                        txParam.setParams(Collections.singletonList(sign));
                        return JsonApiWrapper.serviceApi().requestByParam(txParam); //第四步 发送广播
                    }
                })
                .map(new Func1<HttpResult<String>, HttpResult<String>>() {//本地存储增加nonce
                    @Override
                    public HttpResult<String> call(HttpResult<String> stringHttpResult) {
                        SPUtil.increaseNonce(nonce);
                        return stringHttpResult;
                    }
                })
                .compose(new DefaultTransformer<HttpResult<String>>())
                .subscribe(new ProgressSubscriber<HttpResult<String>>(activity, isShowLoading()) {
                    @Override
                    public void onNext(HttpResult<String> result) {
                        if (isApprove) {//以太坊之外的其他代币修改
                            buy(getCoinAmount(), BuildConfig.ADDRESS_X0);
                        } else {
                            if (listener != null) {
                                mTxLogModel.txHash = result.getResult();

                                TradeResult tradeResult = new TradeResult();
                                tradeResult.setHash(result.getResult());
                                tradeResult.txLogModel = mTxLogModel;
                                listener.success(tradeResult);
                            }
                        }
                    }
                });
    }

    /**
     * App提交交易
     *
     * @param pwd
     * @param limit
     * @param function
     */
    private void rawSend(final String pwd,
                         final BigInteger value,
                         final String limit,
                         final Function function,
                         final String contractAddress,
                         final WalletTx walletTx) {


        rawSign(pwd, value, limit, function, contractAddress)
                .flatMap(new Func1<String, Observable<HttpResult<String>>>() {
                    @Override
                    public Observable<HttpResult<String>> call(String sign) {
                        //数据签名，提交交易
                        final RequestParam txParam = new RequestParam();
                        txParam.setId(3);
                        txParam.setJsonrpc(IConstants.JSON_RPC_VERSION);
                        txParam.setMethod(IConstants.CONTRACTSERVICE_SENDRAWTRANSACTION);
                        txParam.setParams(Collections.singletonList(sign));
                        return JsonApiWrapper.serviceApi().requestByParam(txParam); //第四步 发送广播
                    }
                })
                .map(new Func1<HttpResult<String>, HttpResult<String>>() {//本地存储增加nonce
                    @Override
                    public HttpResult<String> call(HttpResult<String> stringHttpResult) {
                        SPUtil.increaseNonce(nonce);
                        return stringHttpResult;
                    }
                })
                .flatMap(new Func1<HttpResult<String>, Observable<String>>() {
                    @Override
                    public Observable<String> call(HttpResult<String> result) {
                        String hash = result.getResult();
                        walletTx.txHash = hash;
                        walletTx.txReceiptStatus = 2;
                        walletTx.balance = getAssetBalance();//余额减去发送量
                        CustomApplication.getDb().txDao().insertTx(walletTx);//将交易保存到本地
                        return JsonApiWrapper.serviceApi().addtx(BuildConfig.TX_URL_API + "/addtx", walletTx);
                    }
                })
                .map(new Func1<String, QRResult>() {
                    @Override
                    public QRResult call(String s) {
                        return new QRResult(walletTx.txHash);
                    }
                })
                .compose(new DefaultTransformer<QRResult>())
                .subscribe(new ProgressSubscriber<QRResult>(activity, isShowLoading()) {
                    @Override
                    public void onNext(QRResult result) {
                        if (transferListener != null) {
                            //TODO 处理转账回调
                            transferListener.success(result);
                        }
                    }
                });
    }

    /**
     * 扫码Web提交交易
     *
     * @param pwd
     * @param value
     * @param limit
     * @param function
     * @param contractAddress
     * @param isApprove
     */
    private void rawQR(final String pwd,
                       final BigInteger value,
                       final String limit,
                       final Function function,
                       final String contractAddress,
                       final boolean isApprove) {

        rawSign(pwd, value, limit, function, contractAddress)
                .map(new Func1<String, QRRequest>() {
                    @Override
                    public QRRequest call(String txData) {
                        QRRequest request = QRRequest.createPayRequest(qrURl, true);
                        request.txdata = txData;
                        StringBuilder builder = new StringBuilder();
                        builder.append(request.address).append("&");
                        builder.append(request.nonce).append("&");
                        builder.append(request.confirm).append("&");
                        builder.append(request.txdata).append("&");//TODO Hash
                        builder.append(AddressUtils.getMD5Key(activity));
                        String newSign = AddressUtils.md5(builder.toString());
                        request.sign = newSign;
                        return request;
                    }
                })
                .flatMap(new Func1<QRRequest, Observable<QRResult>>() {
                    @Override
                    public Observable<QRResult> call(QRRequest request) {
                        return JsonApiWrapper
                                .serviceApi()
                                .QRPayConfirm(BuildConfig.QR_URL_API + "/qrpay_confirm", request);
                    }
                })
                .map(new Func1<QRResult, QRResult>() {//本地存储增加nonce
                    @Override
                    public QRResult call(QRResult result) {
                        SPUtil.increaseNonce(nonce);
                        return result;
                    }
                })
                .compose(new DefaultTransformer<QRResult>())
                .subscribe(new ProgressSubscriber<QRResult>(activity, isShowLoading()) {
                    @Override
                    public void onNext(QRResult result) {
                        if (qrPayListener != null) {
                            if (isApprove) {//以太坊之外的其他代币修改
                                buy(getCoinAmount(), BuildConfig.ADDRESS_X0);
                            } else {
                                if (result.isOk()) {
                                    qrPayListener.success(result);
                                } else {
                                    qrPayListener.error(result);
                                }
                            }
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        super.onError(e);
                        qrPayListener.error(new QRResult(new QRResult.Error(e.getMessage())));
                    }
                });
    }

    /**
     * 签名交易
     *
     * @param pwd
     * @param value
     * @param limit
     * @param function
     * @param contractAddress
     * @return
     */
    public Observable<String> rawSign(final String pwd,
                                      final BigInteger value,
                                      final String limit,
                                      final Function function,
                                      final String contractAddress) {
        return rawSign(pwd, value, limit, function, contractAddress, null);
    }

    /**
     * 签名交易
     *
     * @param pwd
     * @param value
     * @param limit
     * @param function
     * @param contractAddress
     * @param tx
     * @return
     */
    public Observable<String> rawSign(final String pwd,
                                      final BigInteger value,
                                      final String limit,
                                      final Function function,
                                      final String contractAddress, final WalletTx tx) {

        List<String> address = Collections.singletonList(User.getInstance().getAddress());

        final RequestParam nonceParam = new RequestParam();
        nonceParam.setId(1);
        nonceParam.setJsonrpc(IConstants.JSON_RPC_VERSION);
        nonceParam.setMethod(IConstants.CONTRACTSERVICE_GETNONCE);
        nonceParam.setParams(address);

        //获取Nonce
        Observable nonceObservable =
                JsonApiWrapper
                        .serviceApi()
                        .requestByParam(nonceParam);
        final RequestParam gasParam = new RequestParam();
        gasParam.setId(2);
        gasParam.setJsonrpc(IConstants.JSON_RPC_VERSION);
        gasParam.setMethod(IConstants.CONTRACTSERVICE_GASPRICE);
        gasParam.setParams(address);

        //gas price
        Observable gasPriceObservable =
                JsonApiWrapper
                        .serviceApi()
                        .requestByParam(gasParam);

        return Observable
                .zip(nonceObservable,
                        gasPriceObservable,
                        new Func2<HttpResult<String>, HttpResult<String>, Dex4dTransaction>() {
                            @Override
                            public Dex4dTransaction call(HttpResult<String> o, HttpResult<String> o2) {
                                Dex4dTransaction dex4dTransaction = new Dex4dTransaction(limit);
                                dex4dTransaction.setNonce(o.getResult());
                                nonce = dex4dTransaction.nonce;
                                dex4dTransaction.setGasPrice(o2.getResult());
                                if (tx != null) {
                                    tx.nonce = nonce.toString();
                                    tx.gasPrice = o2.getResult();
                                }
                                return dex4dTransaction;
                            }
                        })
                .map(new Func1<Dex4dTransaction, String>() {
                    @Override
                    public String call(Dex4dTransaction o) {
                        String data = function == null ? "" : FunctionEncoder.encode(function);
                        RawTransaction rawTransaction =
                                RawTransaction.createTransaction(
                                        o.nonce,
                                        o.gasPrice,
                                        o.gasLimit,
                                        contractAddress,
                                        value,
                                        data);
                        if (mTxLogModel!=null){
                            mTxLogModel.data = data;
                            mTxLogModel.gasPrice = o.gasPrice.toString();
                        }
                        //数据签名
                        Credentials credentials = loadCredentials(pwd);
                        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
                        String signData = Numeric.toHexString(signedMessage);
                        if (tx != null) tx.data = signData;
                        return signData;
                    }
                });
    }

    public void send(String to, String amount) {
        String limit = TextUtils.isEmpty(getTokenContractAddress()) ? "21000" : "144000";
        BigInteger value = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger();

        final WalletTx walletTx = WalletTx.createOut();
        walletTx.symbol = getSymbol().toUpperCase();
        walletTx.gasLimit = limit;
        walletTx.value = amount;
        walletTx.to = to;

        if (TextUtils.equals(symbol, ETH)) {
            rawSend(pwd, value, limit, null, to, walletTx);
        } else {
            final Function function = new Function(
                    "transfer",
                    Arrays.<Type>asList(new Address(to),
                            new Uint256(value)),
                    Collections.<TypeReference<?>>emptyList());
            rawSend(pwd, BigInteger.ZERO, limit, function, getTokenContractAddress(), walletTx);
        }
    }

    /**
     * 代币购买DEX4D的时候需要用 “自身的contractAddress” 进行一个approve
     *
     * @param amount
     */
    public void approve(String amount) {
        String limit = getHexGasLimit(TradeResult.FLAG_APPROVE);
        //记录交易日志
        mTxLogModel = new D4DTxLogModel(D4DTxLogModel.TradeType.APPROVE);
        mTxLogModel.gasLimit = limit;
        mTxLogModel.symbol = symbol;
        mTxLogModel.amountA = tokenAmount;
        mTxLogModel.amountB = coinAmount;
        mTxLogModel.to = BuildConfig.CONTRACT_DEX4D_PROTOCOL;

        BigInteger value = new BigInteger(amount);
        final Function function = new Function(
                "approve",
                Arrays.<Type>asList(new Address(getSpenderAddress()),
                        new Uint256(value)),
                Collections.<TypeReference<?>>emptyList());
        raw(pwd, BigInteger.ZERO, limit, function, getTokenContractAddress(), true);
    }

    /**
     * @param coinAmount
     * @param referredBy  推荐人
     */
    public void buy(String coinAmount, String referredBy) {
        String limit = getHexGasLimit(TradeResult.FLAG_BUY);
        mTxLogModel = new D4DTxLogModel(D4DTxLogModel.TradeType.BUY);
        mTxLogModel.gasLimit = limit;
        mTxLogModel.symbol = symbol;
        mTxLogModel.amountA = tokenAmount;
        mTxLogModel.amountB = coinAmount;
        mTxLogModel.to = BuildConfig.CONTRACT_DEX4D_PROTOCOL;

        BigInteger value = new BigInteger(coinAmount);
        final Function function = new Function(
                "buy",
                Arrays.<Type>asList(
                        new Utf8String(symbol), //用 symbol 币去购买D4D
                        new Uint256(value),
                        new Address(referredBy)
                ),
                Collections.<TypeReference<?>>emptyList());

        if (platform == P_WEB_QR) {
            if (TextUtils.equals(symbol, ETH)) {
                rawQR(pwd, value, limit, function, BuildConfig.CONTRACT_DEX4D_PROTOCOL, false);
            } else {
                rawQR(pwd, BigInteger.ZERO, limit, function, BuildConfig.CONTRACT_DEX4D_PROTOCOL, false);
            }
        } else {
            if (TextUtils.equals(symbol, ETH)) {
                raw(pwd, value, limit, function, BuildConfig.CONTRACT_DEX4D_PROTOCOL, false);
            } else {
                raw(pwd, BigInteger.ZERO, limit, function, BuildConfig.CONTRACT_DEX4D_PROTOCOL, false);
            }
        }

    }

    /**
     * 购买推荐权限(我的模块中)
     *
     * @param amount        支付金额，单位十进制的以太
     * @param referralAlias 推荐别名（购买权限的钱包持有人）
     */
    public void buyReferral(String amount, String referralAlias) {
        String limit = getHexGasLimit(TradeResult.FLAG_BUY_REFERRAL);
        BigInteger value = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger();

        //记录交易日志
        mTxLogModel = new D4DTxLogModel(D4DTxLogModel.TradeType.BUYREFERRAL);
        mTxLogModel.symbol = "eth";
        mTxLogModel.amountA = value.toString();
        mTxLogModel.amountB = value.toString();
        mTxLogModel.to = BuildConfig.CONTRACT_DEX4D_PROTOCOL;
        mTxLogModel.gasLimit = limit;

        final Function function = new Function(
                "buyReferral",
                Arrays.<Type>asList(new Utf8String(referralAlias)),
                Collections.<TypeReference<?>>emptyList());

        if (platform == P_WEB_QR) {
            rawQR(pwd, value, limit, function, BuildConfig.CONTRACT_DEX4D_REFERRALER, false);
        } else {
            raw(pwd, value, limit, function, BuildConfig.CONTRACT_DEX4D_REFERRALER, false);
        }

    }

    /**
     * 购买对倒权限(我的模块中)
     *
     * @param amount 支付金额，单位十进制的以太
     */
    public void buyArbitrage(String amount) {
        String limit = getHexGasLimit(TradeResult.FLAG_BUY_SWAP);
        BigInteger value = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger();

        //记录交易日志
        mTxLogModel = new D4DTxLogModel(D4DTxLogModel.TradeType.BUYSWAP);
        mTxLogModel.symbol = "eth";
        mTxLogModel.amountA = value.toString();
        mTxLogModel.amountB = value.toString();
        mTxLogModel.to = BuildConfig.CONTRACT_DEX4D_PROTOCOL;
        mTxLogModel.gasLimit = limit;

        final Function function = new Function(
                "buyArbitrage",
                Collections.<Type>emptyList(),
                Collections.<TypeReference<?>>emptyList());
        if (platform == P_WEB_QR) {
            rawQR(pwd, value, limit, function, BuildConfig.CONTRACT_DEX4D_REFERRALER, false);
        } else {
            raw(pwd, value, limit, function, BuildConfig.CONTRACT_DEX4D_REFERRALER, false);
        }

    }

    /**
     * 卖出
     *
     * @param dex4dAmount 卖出的DEX4D数量 单位十进制的以太
     */
    public void sell(String dex4dAmount) {
        String limit = getHexGasLimit(TradeResult.FLAG_SELL);
        //记录交易日志
        mTxLogModel = new D4DTxLogModel(D4DTxLogModel.TradeType.SELL);
        mTxLogModel.symbol = symbol;
        mTxLogModel.amountA = tokenAmount;
        mTxLogModel.amountB = coinAmount;
        mTxLogModel.to = BuildConfig.CONTRACT_DEX4D_PROTOCOL;
        mTxLogModel.gasLimit = limit;

        BigInteger value = Convert.toWei(dex4dAmount, Convert.Unit.ETHER).toBigInteger();
        final Function function = new Function(
                "sell",
                Arrays.<Type>asList(
                        new Utf8String(symbol), //卖出得到的币
                        new Uint256(value)
                ),
                Collections.<TypeReference<?>>emptyList());
        if (platform == P_WEB_QR) {
            rawQR(pwd, BigInteger.ZERO, limit, function, BuildConfig.CONTRACT_DEX4D_PROTOCOL, false);
        } else {
            raw(pwd, BigInteger.ZERO, limit, function, BuildConfig.CONTRACT_DEX4D_PROTOCOL, false);
        }
    }

    /**
     * 对倒
     *
     * @param toSymbol    对倒得到的币
     * @param dex4dAmount 卖出的DEX4D数量 单位十进制的以太
     */
    public void swap(String toSymbol, String dex4dAmount) {
        String limit = getHexGasLimit(TradeResult.FLAG_SWAP);
        //记录交易日志
        mTxLogModel = new D4DTxLogModel(D4DTxLogModel.TradeType.SWAP);
        mTxLogModel.symbol = symbol;
        mTxLogModel.amountA = tokenAmount;
        mTxLogModel.amountB = coinAmount;
        mTxLogModel.to = BuildConfig.CONTRACT_DEX4D_PROTOCOL;
        mTxLogModel.gasLimit = limit;
        mTxLogModel.swapA = symbol;
        mTxLogModel.swapB = toSymbol;

        BigInteger value = Convert.toWei(dex4dAmount, Convert.Unit.ETHER).toBigInteger();
        final Function function = new Function(
                "arbitrageTokens",
                Arrays.<Type>asList(
                        new Utf8String(symbol), //从哪个池子里的dex4d
                        new Utf8String(toSymbol), //对倒得到的币
                        new Uint256(value)
                ),
                Collections.<TypeReference<?>>emptyList());
        if (platform == P_WEB_QR) {
            rawQR(pwd, BigInteger.ZERO, limit, function, BuildConfig.CONTRACT_DEX4D_PROTOCOL, false);
        } else {
            raw(pwd, BigInteger.ZERO, limit, function, BuildConfig.CONTRACT_DEX4D_PROTOCOL, false);
        }
    }

    /**
     * 提现
     * 从DEX4D池子里提现到钱包中
     *
     * @param withdrawSymbol 提现币种
     * @param amount         提现金额，单位十进制
     */
    public void withdraw(String withdrawSymbol, String amount) {
        String limit = getHexGasLimit(TradeResult.FLAG_WITHDRAW);
        BigInteger value = Convert.toWei(amount, Convert.Unit.ETHER).toBigInteger();

        //记录交易日志
        mTxLogModel = new D4DTxLogModel(D4DTxLogModel.TradeType.WITHDRAW);
        mTxLogModel.symbol = withdrawSymbol;
        mTxLogModel.amountA = value.toString();
        mTxLogModel.amountB = value.toString();
        mTxLogModel.to = BuildConfig.CONTRACT_DEX4D_PROTOCOL;
        mTxLogModel.gasLimit = limit;

        final Function function = new Function(
                "withdraw",
                Arrays.<Type>asList(new Utf8String(withdrawSymbol), //提现的币种
                        new Uint256(value)),  //提现的金额
                Collections.<TypeReference<?>>emptyList());

        if (platform == P_WEB_QR) {
            rawQR(pwd, BigInteger.ZERO, limit, function, BuildConfig.CONTRACT_DEX4D_PROTOCOL, false);
        } else {
            raw(pwd, BigInteger.ZERO, limit, function, BuildConfig.CONTRACT_DEX4D_PROTOCOL, false);
        }
    }

    /**
     * 加载证书
     *
     * @param password
     * @return
     */
    private Credentials loadCredentials(String password) {
        String keystore = User.getInstance().getKeystore();
        String pwd = password + EthWalletUtil.getSignature().substring(0, 18);
        try {
            keystore = CryptoUtils.decrypt(keystore, pwd); //解密
        } catch (Exception e) {
            e.printStackTrace();
        }
        String priKey = EthWalletUtil.getPriKey(keystore, password);
        return Credentials.create(priKey);
    }

    public void reinvest() {
        String limit = getHexGasLimit(TradeResult.FLAG_REINVEST);
    }

    public void sell() {
        String limit = getHexGasLimit(TradeResult.FLAG_SELL);
    }

    public void swap() {
        String limit = getHexGasLimit(TradeResult.FLAG_SWAP);
    }

    public void withdraw() {
        String limit = getHexGasLimit(TradeResult.FLAG_WITHDRAW);
    }

    public void withdrawAll() {
        String limit = getHexGasLimit(TradeResult.FLAG_WITHDRAW_ALL);
    }

    public static String getGasLimit(int flag) {
        return convert(getHexGasLimit(flag));
    }

    public static String getHexGasLimit(int flag) {
        if (flag == TradeResult.FLAG_BUY) {
            return "0x55730";
        } else if (flag == TradeResult.FLAG_SELL) {
            return "0x1adb0";
        } else if (flag == TradeResult.FLAG_REINVEST) {
            return "0x30d40";
        } else if (flag == TradeResult.FLAG_SWAP) {
            return "0x668a0";
        } else if (flag == TradeResult.FLAG_WITHDRAW) {
            return "0x19e10";
        } else if (flag == TradeResult.FLAG_WITHDRAW_ALL) {
            return "0x493e0";
        } else if (flag == TradeResult.FLAG_APPROVE) {
            return "0x186a0";
        } else if (flag == TradeResult.FLAG_BUY_REFERRAL) {
            return "0x55730"; //todo 修改
        } else if (flag == TradeResult.FLAG_BUY_SWAP) {
            return "0x55730"; //todo 修改
        }
        return "0x0";
    }

    private static String convert(String limit) {
        return Convert.fromWei(Numeric.toBigInt(limit).toString(), Convert.Unit.ETHER).setScale(6, RoundingMode.CEILING).toString();
    }


    public interface OnTradeListener {
        public void success(TradeResult result);

        public void error(TradeResult result);
    }

    public interface OnTransferListener {
        public void success(QRResult result);
        public void error(QRResult result);
    }

    public boolean isShowLoading() {
        return platform == P_WEB_QR ? false : true;
    }

}
